// oop2
using namespace std;
//-------------------------------------------------------------8 member template
#include <string>
#include <iostream>
#include <memory> // shared_ptr

namespace oop201 {
class Base1 {};
class Derived1 : public Base1 {};
class Base2 {};
class Derived2 : public Base2 {};

template <class T1, class T2>
struct pair {
    typedef T1 first_type;
    typedef T2 second_type;
    
    T1 first;
    T2 second;
    pair() : first(T1()), second(T2()) { cout << "pair()" << endl; };
    pair(const T1& a, const T2& b) : first(a), second(b) { cout << "pair(const T1&, const T2&)" <<endl; }
   
    template <typename U1, typename U2>
    pair(const pair<U1, U2>& p) : first(p.first), second(p.second) { cout << "pair(const pair<U1, U2>&)" << endl; }
};

void test_member_template() {
    cout << "test_member_template()" << endl;
    pair<Derived1, Derived2> p;
    pair<Base1, Base2> p2(pair<Derived1, Derived2>());
    pair<Base1, Base2> p3(p);

    //Derived1 will be assigned to Base1; Derived2 will be assigned to Base2.
	//OO allow such assignments since of "is-a" relationship between them.  
		
	//!	pair<Derived1, Derived2> p4(p3);
	//error messages as below appear at the ctor statements of pair member template
	// [Error] no matching function for call to 'Derived1::Derived1(const Base1&)'
	// [Error] no matching function for call to 'Derived2::Derived2(const Base2&)'		

    Base1* ptr = new Derived1; // up-cast
    shared_ptr<Base1> sptr(new Derived1); // simulate up-cast 模仿向上转型
}
}
//------------------------------------------------------------- 54 template template parameter
#include <string>
#include <iostream>
#include <list>
namespace oop202 {
template<typename T,
        // template <typename T> 可能或报错，详见 issues #10
        template <typename TT>
            class Container
        >
class XCls {
private:
    Container<T> c;
public:
    XCls() {
        cout << "XCls()" << endl;
        for (long i = 0; i < 100; ++i) {
            c.insert(c.end(), T());
        }
    }
};

template<typename T>
using Lst = list<T, allocator<T>>;

void test_template_template_parameters() {
    cout << "test_template_template_parameters" << endl;
    //!	XCls<string, list> mylist;
    //[Error] expected a template of type 'template<class T> class Container', got 'template<class _Tp, class _Alloc> class std::list'
    XCls<string, Lst> myList;
}
}
//------------------------------------------------------------- 89 ttp with smart ptr
#include <string>
#include <iostream>
#include <memory> // smart pointer
namespace oop203 {
template <typename T,
        // template <typename T> error
         template <typename TT>
            class SmartPtr
        >
class XCls {
private:
    SmartPtr<T> sp;
public:
    XCls() : sp(new T) { cout << "XCls()" << endl; }
};

void test_ttp() {
    cout << "test_ttp()" << endl;
    XCls<string, shared_ptr> p1;
    XCls<double, auto_ptr> p2;
    // XCls<double, unique_ptr> p3; // unique_ptr has two template parameters 
    // XCls<int, weak_ptr> p4; // [Error] no matching function for call to 'std::weak_ptr<int>::weak_ptr(int*)'
}
}


//------------------------------------------------------------- 129 Fraction
/**
conversion function 转换函数
只要认为合理可以有多个 conversion function，转 double、flout、string ……
 */ 

#include <iostream>
namespace oop205 {
class Fraction {
private:
    int m_numerator; // 分子
    int m_denominator; // 分母
public:
    // explicit 关键字用于修饰单参数的构造函数，它的作用是只允许显式调用构造函数，防止编译器进行隐式类型转换。
    // 防止 int -> Fraction，Fraction f = 10;
    explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {
        cout << m_numerator << "\t" << m_denominator << endl;
    }
    
    // Fraction -> double，编译器需要将 Fraction 转换成 double 时调用，转出去
    // 可以转换成任意的 type (只要出现过)，返回 double 类型，c++不需要再写返回值类型，已经有 double，可以防止类型不匹配
    // 没有形参(转换函数只是转换类型)，常函数(不应该修改data)
    operator double() const {
        return (double) m_numerator / m_denominator;
    }

    Fraction operator+(const Fraction& f) {
        cout << "operator+(): " << f.m_numerator << "\t" << f.m_denominator << endl;
        // ... plus
        return f;
    }
};

void test_conversion_functions() {
    cout << "test_conversion_functions()" << endl;
    
    Fraction f(3, 5);
    // 编译器先寻找 global operator+(int, Fraction)，如果没有找到，再寻找转换函数
    double d = 4 + f; // 4.6
    cout << d << endl;

    // ! Fraction d2 = f + 4; // ambiguous
} 
}
//------------------------------------------------------------- 172 reference
#include <iostream>
namespace oop206 {
void test_reference() {
    cout << "test_reference()" << endl;
    
    {
	int x=0;
	int* p = &x;
	int& r = x;   	//r is a reference to x. now r==x==0
	int x2=5;
	r = x2;      	//r cannot re-reference to another. now r==x==5
	int& r2 = r; 	//now r2==5 (r2 is a reference to r, i.e. a reference to x)	
	}
	
	
	{
    typedef struct Stag { int a,b,c,d; } S;

	double x=0;
	double* p = &x; 	//p is a pointer to x. p's value is same as x's address.
	double& r = x; 		//r is a reference to x, now r==x==0
	
	cout << sizeof(x) << endl; 	//8
	cout << sizeof(p) << endl; 	//4
	cout << sizeof(r) << endl; 	//8
	cout << p << endl; 			//0065FDFC
	cout << *p << endl; 		//0
	cout << x << endl; 			//0
	cout << r << endl; 			//0
	cout << &x << endl; 		//0065FDFC
	cout << &r << endl; 		//0065FDFC
	
	S s;
	S& rs = s;
	cout << sizeof(s) << endl; 	//16
	cout << sizeof(rs) << endl; //16
	cout << &s << endl; 		//0065FDE8
	cout << &rs << endl; 		//0065FDE8	
 	}
}
}

//-------------------------------------------------------------
int main(int argc, char const *argv[]) {

    std::cout << __cplusplus << endl; // 201402

    oop201::test_member_template();
    oop202::test_template_template_parameters();
    oop203::test_ttp(); 
    oop205::test_conversion_functions(); 
    oop206::test_reference();

    // auto a; // error: declaration of 'auto a' has no initializer
    
    return 0;
}
